

package com.kedacom.ctsp.authz.oauth2.controller;

import com.kedacom.ctsp.authority.entity.Department;
import com.kedacom.ctsp.authority.entity.Resource;
import com.kedacom.ctsp.authority.entity.User;
import com.kedacom.ctsp.authority.service.DepartmentService;
import com.kedacom.ctsp.authority.service.UserService;
import com.kedacom.ctsp.authority.vo.DepartmentVO;
import com.kedacom.ctsp.authority.vo.ResourceVO;
import com.kedacom.ctsp.authz.AuthenticationService;
import com.kedacom.ctsp.authz.entity.AuthResource;
import com.kedacom.ctsp.authz.entity.Authentication;
import com.kedacom.ctsp.authz.entity.AuthzTypeEnum;
import com.kedacom.ctsp.authz.exception.UnauthorizedException;
import com.kedacom.ctsp.authz.oauth2.core.ErrorType;
import com.kedacom.ctsp.authz.oauth2.core.OAuth2Authentication;
import com.kedacom.ctsp.authz.oauth2.core.OAuth2Exception;
import com.kedacom.ctsp.authz.oauth2.core.vo.*;
import com.kedacom.ctsp.authz.oauth2.entity.AccessToken;
import com.kedacom.ctsp.authz.oauth2.provider.OAuth2UserResourceQequestFilter;
import com.kedacom.ctsp.authz.oauth2.service.AccessTokenService;
import com.kedacom.ctsp.authz.oauth2.service.ClientService;
import com.kedacom.ctsp.authz.oauth2.service.OAuth2AuthenticationService;
import com.kedacom.ctsp.authz.oauth2.service.OAuthResourceService;
import com.kedacom.ctsp.authz.oauth2.util.JWTHelper;
import com.kedacom.ctsp.lang.mapper.BeanMapper;
import com.kedacom.ctsp.logging.AccessLogger;
import com.kedacom.ctsp.web.controller.message.ResponseMessage;
import com.kedacom.ctsp.web.controller.util.QueryParamUtil;
import com.kedacom.ctsp.web.entity.TreeSupportBean;
import com.kedacom.ctsp.web.entity.page.PagerResult;
import com.kedacom.ctsp.web.entity.param.QueryParamEntity;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.kedacom.ctsp.authority.entity.Role.CLIENTSIGN;
import static com.kedacom.ctsp.web.controller.message.ResponseMessage.ok;

/**
 * TODO 完成注释
 *
 * @author zhouhao
 */
@RestController
@Api(tags = "kedacom-oauth2", description = "OAuth2授权", hidden = true)
@RequestMapping("${commons.web.mappings.oauth2_resource:resources}")
public class OAuth2ResourceController {

    @Autowired
    private AccessTokenService accessTokenService;
    @Autowired
    private ClientService clientService;
    @Autowired
    private OAuth2AuthenticationService oAuth2AuthenticationService;
    @Autowired
    private UserService userService;
    @Autowired
    private OAuthResourceService oAuthResourceService;
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    protected AuthenticationService authenticationService;

    @Autowired
    private JWTHelper jwtHelper;

    @Autowired
    private OAuth2UserResourceQequestFilter oAuth2UserResourceQequestFilter;

    @PostMapping(path = "/getLoginUser")
    @ApiOperation("根据accessToken获取登录信息")
    public ResponseMessage<Authentication> getLoginUser(@RequestBody UserQueryParam param) {
        AccessToken auth2AccessEntity = accessTokenService.getTokenByAccessToken(param.getAccessToken());
        if (null == auth2AccessEntity) {
            throw new UnauthorizedException();
        }
        User user = userService.get(auth2AccessEntity.getOwnerId());
        return ResponseMessage.ok(oAuth2AuthenticationService.loadUserByUsername(user.getUsername(), param.getClientId()));
    }

    @PostMapping(path = "/getLoginUserByJwt")
    @ApiOperation("根据jwt获取登录信息")
    public ResponseMessage<Authentication> getLoginUserByJwt(@RequestBody JwtQueryParam param) {
        if (param.getJwtToken() == null) {
            throw new UnauthorizedException();
        }
        String username;
        try {
            username = jwtHelper.getInfoFromToken(param.getJwtToken());
        } catch (Exception e) {
            throw new OAuth2Exception(ErrorType.ILLEGAL_JWT_TOKEN, e.getMessage());
        }
        return ResponseMessage.ok(oAuth2AuthenticationService.loadUserByUsername(username, param.getClientId()));
    }

    @PostMapping("/user")
    @ApiOperation("根据accessToken获取用户信息")
    public ResponseMessage<PagerResult<PlainUser>> queryUser(@RequestBody ClientQueryParam param) {

        return ResponseMessage.ok(oAuthResourceService.queryUser(param));
    }

    @PostMapping("/department")
    @ApiOperation("根据accessToken获取部门信息")
    public ResponseMessage<PagerResult<PlainDepartment>> queryDepartment(@RequestBody ClientQueryParam param) {

        return ResponseMessage.ok(oAuthResourceService.queryDepartment(param));
    }

    @PostMapping("/person")
    @ApiOperation("根据accessToken获取人员信息")
    public ResponseMessage<PagerResult<PlainPerson>> queryPerson(@RequestBody ClientQueryParam param) {

        return ResponseMessage.ok(oAuthResourceService.queryPerson(param));
    }

    @PostMapping("/role")
    @ApiOperation("根据accessToken获取角色信息")
    public ResponseMessage<PagerResult<PlainRole>> queryRole(@RequestBody ClientQueryParam param) {

        return ResponseMessage.ok(oAuthResourceService.queryRole(param));
    }

    @PostMapping("/role_by_user")
    @ApiOperation("根据accessToken获取角色信息")
    public ResponseMessage<PagerResult<PlainRole>> queryRoleByUser(@RequestBody UserQueryParam param) {

        return ResponseMessage.ok(oAuthResourceService.queryRoleByUser(param));
    }

    @PostMapping("/resourceSetting")
    @ApiOperation("根据accessToken获取资源权限信息")
    public ResponseMessage<PagerResult<PlainResourceSetting>> queryResourceSetting(@RequestBody ClientQueryParam param) {

        param.and(CLIENTSIGN, param.getClientId());

        return ResponseMessage.ok(oAuthResourceService.queryResourceSetting(param));
    }

    /**
     * 前登录用户拥有的资源(树形列表)
     * 不支持分页
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/tree/me")
    @AccessLogger("按条件获取当前登录用户拥有的树形资源")
    @ApiOperation("按条件获取当前登录用户拥有的树形资源")
    @ApiResponses({
            @ApiResponse(code = 200, message = "查询成功"),
            @ApiResponse(code = 401, message = "未授权"),
            @ApiResponse(code = 403, message = "无权限"),
            @ApiResponse(code = 404, message = "数据不存在")
    })
    public ResponseMessage<List<ResourceVO>> listMyAsTree(@RequestBody ClientQueryParam param) {

        return oAuthResourceService.listMyAsTree(param);
    }

    @PutMapping(path = "/password")
    @AccessLogger("{update_password_login_user}")
    @ApiOperation("修改当前用户的密码")
    public ResponseMessage<Object> updatePassword(@RequestBody Map map) {
        String oldPassword = map.get("oldPassword") == null ? "" : map.get("oldPassword").toString();
        String password = map.get("password") == null ? "" : map.get("password").toString();
        String userId = map.get("userId") == null ? "" : map.get("userId").toString();
        Assert.notNull(oldPassword, "auth.user.oldPassword.isnull");
        Assert.notNull(password, "auth.user.password.isnull");
        return ResponseMessage.ok(oAuthResourceService.updatePassword(userId, oldPassword, password));
    }

    @PostMapping(path = "/client/list")
    @AccessLogger("获取客户端列表信息")
    @ApiOperation("获取客户端列表信息")
    @ApiResponses({
            @ApiResponse(code = 200, message = "查询成功"),
            @ApiResponse(code = 401, message = "未授权"),
            @ApiResponse(code = 403, message = "无权限"),
            @ApiResponse(code = 404, message = "数据不存在")
    })
    public ResponseMessage<PagerResult<PlainClient>> clientList(@RequestBody QueryParamEntity param, HttpServletRequest request) {
        QueryParamUtil.paddingTerms(param, request);
        return oAuthResourceService.clientList(param);

    }

    @PostMapping(path = "/client/userClient")
    @AccessLogger("获取用户绑定的项目")
    @ApiOperation("获取用户绑定的项目")
    @ApiResponses({
            @ApiResponse(code = 200, message = "查询成功"),
            @ApiResponse(code = 401, message = "未授权"),
            @ApiResponse(code = 403, message = "无权限"),
            @ApiResponse(code = 404, message = "数据不存在")
    })
    public ResponseMessage<List<PlainClient>> getUserClient(@RequestBody UserQueryParam param) {

        return ResponseMessage.ok(oAuthResourceService.getUserClient(param));
    }

    @PostMapping(path = "/loadResources")
    @ApiOperation("根据accessToken获取权限资源信息")
    public ResponseMessage<List<AuthResource>> loadResources(@RequestBody AuthResourceQueryParam authResourceQueryParam) {
        return ResponseMessage.ok(authenticationService.loadResources(authResourceQueryParam.getAuthentication(), authResourceQueryParam.getResources(), AuthzTypeEnum.ANNOTATION));
    }

    @PostMapping(path = "/authResource")
    @ApiOperation("根据accessToken获取资源信息")
    public ResponseMessage<Set<AuthResource>> getAuthResource(@RequestBody OAuth2Authentication authentication) {
        return ResponseMessage.ok(authenticationService.getAuthResource(authentication));
    }


    /**
     * 获取部门列表
     * 如果pid不为空, 查找指定父级下的所有子节点
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/dept")
    ResponseMessage<PagerResult<PlainDepartment>> queryDept(@RequestParam(required = false) String pid, @RequestBody ClientQueryParam param) {

        PagerResult<Department> pageDepts = oAuthResourceService.queryDept(pid, param);
        return ResponseMessage.ok(pageDepts.convertTo(e -> BeanMapper.deepMap(e, PlainDepartment.class)));

    }

    /**
     * 通过指定用户获取部门列表
     * 如果pid不为空, 查找指定父级下的所有子节点
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/dept_by_user")
    ResponseMessage<PagerResult<PlainDepartment>> queryDept(@RequestParam(required = false) String pid, @RequestBody UserQueryParam param) {

        //Fegin 通过 accessToken 无登录状态获取用户本人部门、资源等信息，通过权限资源过滤
        oAuth2UserResourceQequestFilter.getUserResourceByaccessTokenFiler(param);

        PagerResult<Department> pageDepts = oAuthResourceService.queryDeptByUser(pid, param);
        return ResponseMessage.ok(pageDepts.convertTo(e -> BeanMapper.deepMap(e, PlainDepartment.class)));
    }

    /**
     * 获取部门树
     * 如果pid不为空, 查找指定父级下的所有子节点
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/dept/tree")
    ResponseMessage<List<DepartmentVO>> queryDeptAsTree(@RequestParam(required = false) String pid, @RequestBody ClientQueryParam param) {

        List<Department> departments = oAuthResourceService.queryDeptAsTree(pid, param);
        return buildDepartmentTree(departments);

    }

    private ResponseMessage<List<DepartmentVO>> buildDepartmentTree(List<Department> departments) {
        List<DepartmentVO> resourceVOList = departments.stream().map(r -> {
            DepartmentVO departmentVO = departmentService.createInstance(DepartmentVO.class);
            departmentVO.convertFrom(r);
            return departmentVO;
        }).collect(Collectors.toList());

        ResponseMessage<List<DepartmentVO>> result = ok(resourceVOList);
        result.exclude(TreeSupportBean.class, TreeSupportBean.PARENT);
        return result;
    }

    /**
     * 通过指定用户获取部门树
     * 如果pid不为空, 查找指定父级下的所有子节点
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/dept_by_user/tree")
    ResponseMessage<List<DepartmentVO>> queryDeptAsTree(@RequestParam(required = false) String pid, @RequestBody UserQueryParam param) {

        //Fegin 通过 accessToken 无登录状态获取用户本人部门、资源等信息，通过权限资源过滤
        oAuth2UserResourceQequestFilter.getUserResourceByaccessTokenFiler(param);

        List<Department> departments = oAuthResourceService.queryDeptAsTreeByUser(pid, param);
        return buildDepartmentTree(departments);
    }

    /**
     * 获取资源信息
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/permission")
    ResponseMessage<PagerResult<PlainResource>> queryResource(@RequestParam(required = false) String pid, @RequestBody ClientQueryParam param) {

        PagerResult<Resource> pageDepts = oAuthResourceService.queryResource(pid, param);
        return ResponseMessage.ok(pageDepts.convertTo(e -> BeanMapper.deepMap(e, PlainResource.class)));
    }

    /**
     * 通过用户获取资源信息
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/permission_by_user")
    ResponseMessage<PagerResult<PlainResource>> queryResourceByUser(@RequestParam(required = false) String pid, @RequestBody UserQueryParam param) {

        //Fegin 通过 accessToken 无登录状态获取用户本人部门、资源等信息，通过权限资源过滤
        oAuth2UserResourceQequestFilter.getUserResourceByaccessTokenFiler(param);

        PagerResult<Resource> pageDepts = oAuthResourceService.queryResourceByUser(pid, param);
        return ResponseMessage.ok(pageDepts.convertTo(e -> BeanMapper.deepMap(e, PlainResource.class)));
    }

    /**
     * 获取资源信息
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/permission/tree")
    ResponseMessage<List<ResourceVO>> queryResourceAsTree(@RequestParam(required = false) String pid, @RequestBody ClientQueryParam param) {

        List<Resource> resources = oAuthResourceService.queryResourceAsTree(pid, param);
        return buildResourceTree(resources);
    }

    private ResponseMessage<List<ResourceVO>> buildResourceTree(List<Resource> resources) {
        List<ResourceVO> resourceVOList = resources.stream().map(r -> {
            ResourceVO resourceVO = departmentService.createInstance(ResourceVO.class);
            resourceVO.convertFrom(r);
            return resourceVO;
        }).collect(Collectors.toList());

        ResponseMessage<List<ResourceVO>> result = ok(resourceVOList);
        result.exclude(TreeSupportBean.class, TreeSupportBean.PARENT);
        return result;
    }


    /**
     * 通过用户获取资源信息
     *
     * @param param
     * @return
     */
    @PostMapping(path = "/permission_by_user/tree")
    ResponseMessage<List<ResourceVO>> queryResourceAsTreeByUser(@RequestParam(required = false) String pid, @RequestBody UserQueryParam param) {

        //Fegin 通过 accessToken 无登录状态获取用户本人部门、资源等信息，通过权限资源过滤
        oAuth2UserResourceQequestFilter.getUserResourceByaccessTokenFiler(param);
        List<Resource> resources = oAuthResourceService.queryResourceAsTreeByUser(pid, param);
        return buildResourceTree(resources);
    }


    private void validateClient(String clientId, String clientSecret) {
        clientService.validateClient(clientId, clientSecret);
    }

    private AccessToken checkAccessToken(@RequestBody UserQueryParam param) {
        AccessToken accessToken = accessTokenService.getTokenByAccessToken(param.getAccessToken());
        if (null == accessToken) {
            throw new UnauthorizedException();
        }
        return accessToken;
    }
}
